package com.tn.rtdata.jmx;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

import com.tn.rtdata.DataEvent;
import com.tn.rtdata.DataEventStats;

/**
 * An implementation of <code>DataEventStatsInfoMXBean</code> that references the data event stats cache in order to return data event stats
 * information.
 */
public class DataEventStatsInfo implements DataEventStatsInfoMXBean
{
  private Map<String, DataEventStats> dataEventStatsCache;
  private String fillName;
  private DataEvent.Type dataEventType;

  /**
   * Creates a new <code>DataEventStatsInfo</code>.
   */
  public DataEventStatsInfo(Map<String, DataEventStats> dataEventStatsCache, String fillName, DataEvent.Type dataEventType)
  {
    this.dataEventStatsCache = dataEventStatsCache;
    this.fillName = fillName;
    this.dataEventType = dataEventType;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public long getAverage()
  {
    long total = 0;
    long processed = 0;

    for (DataEventStats dataEventStats : new DataEventStatsIterable(dataEventStatsCache, fillName, dataEventType))
    {
      total += dataEventStats.getProcessingTimeAverage() * dataEventStats.getProcessed();
      processed += dataEventStats.getProcessed();
    }

    return processed > 0 ? total / processed : 0;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public long getMinimum()
  {
    long minimum = Long.MAX_VALUE;

    for (DataEventStats dataEventStats : new DataEventStatsIterable(dataEventStatsCache, fillName, dataEventType))
    {
      if (minimum > dataEventStats.getProcessingTimeMinimum())
      {
        minimum = dataEventStats.getProcessingTimeMinimum();
      }
    }

    return minimum;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public long getMaximum()
  {
    long maximum = 0;

    for (DataEventStats dataEventStats : new DataEventStatsIterable(dataEventStatsCache, fillName, dataEventType))
    {
      if (maximum < dataEventStats.getProcessingTimeMaximum())
      {
        maximum = dataEventStats.getProcessingTimeMaximum();
      }
    }

    return maximum;
  }

  /**
   * An implementation of <code>Iterable</code> that returns an <code>Iterator</code> that iterates over events of a given type in the
   * <code>dataEventStatsCache</code>.
   */
  private static class DataEventStatsIterable implements Iterable<DataEventStats>
  {
    private Map<String, DataEventStats> dataEventStatsCache;
    private String fillName;
    private DataEvent.Type dataEventType;

    /**
     * Creates a new <code>DataEventStatsIterable</code> initialized with the <code>dataEventStatsCache</code> and the
     * <code>dataEventType</code> to iterate over.
     */
    public DataEventStatsIterable(Map<String, DataEventStats> dataEventStatsCache, String fillName, DataEvent.Type dataEventType)
    {
      this.dataEventStatsCache = dataEventStatsCache;
      this.fillName = fillName;
      this.dataEventType = dataEventType;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterator<DataEventStats> iterator()
    {
      return new DataEventStatsIterator(dataEventStatsCache, fillName, dataEventType);
    }
  }

  /**
   * An implementation of <code>Iterator</code> that iterates over events of a given type in the <code>dataEventStatsCache</code>.
   */
  private static class DataEventStatsIterator implements Iterator<DataEventStats>
  {
    private Queue<String> dataEventIds;
    private DataEventStats next;
    private Map<String, DataEventStats> dataEventStatsCache;
    private String fillName;
    private DataEvent.Type dataEventType;

    /**
     * Creates a new <code>DataEventStatsIterator</code> initialized with the <code>dataEventStatsCache</code> and the
     * <code>dataEventType</code> to iterate over.
     */
    public DataEventStatsIterator(Map<String, DataEventStats> dataEventStatsCache, String fillName, DataEvent.Type dataEventType)
    {
      this.dataEventStatsCache = dataEventStatsCache;
      this.fillName = fillName;
      this.dataEventType = dataEventType;
      this.dataEventIds = new LinkedList<String>(dataEventStatsCache.keySet());
      this.next = findNext();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean hasNext()
    {
      return next != null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DataEventStats next()
    {
      DataEventStats next = this.next;
      this.next = findNext();
      return next;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void remove()
    {
      throw new UnsupportedOperationException();
    }

    /**
     * Finds the next <code>DataEventStats</code> from the cache.
     */
    private DataEventStats findNext()
    {
      for (String dataEventId = dataEventIds.poll(); dataEventId != null; dataEventId = dataEventIds.poll())
      {
        DataEventStats dataEventStats = dataEventStatsCache.get(dataEventId);

        if (dataEventStats != null && dataEventStats.getFillName().equals(fillName) && dataEventStats.getDataEventType().equals(dataEventType))
        {
          return dataEventStats;
        }
      }

      return null;
    }
  }
}
